package xbuf

import (
	"encoding/binary"
	. "gitee.com/shuohe/xutils"
	"gitee.com/shuohe/xutils/xerror"
	"io"
)

var errNotEnoughData = xerror.SimpleNew("xbuf.Buffer: Not enough available data to read.")

/**
 * 读写缓冲区。
 *
 * @implements:
 * 		io.Reader
 *		io.Writer
 */
type Buffer struct {
	Buf      []byte
	Ri       int
	Wi       int
	AutoGrow bool
}

func (this *Buffer) EnsureWrite(n int) {
	needSize := this.Wi + n
	blen := len(this.Buf)

	// 如果还有足够空间，直接返回
	if needSize <= blen {
		return
	}

	// 如果腾出已读空间后，空间足够
	if this.Ri > 0 && needSize-this.Ri < blen {
		// 腾出已读空间
		copy(this.Buf, this.Buf[this.Ri:this.Wi])
	} else {
		// 肯定放不下，需要另外分配空间
		buf := make([]byte, 2*needSize)
		copy(buf, this.Buf[this.Ri:this.Wi])
		this.Buf = buf
	}

	this.Wi -= this.Ri
	this.Ri = 0
}

func (this *Buffer) Available() int {
	return this.Wi - this.Ri
}

func (this *Buffer) Bytes() []byte {
	return this.Buf[this.Ri:this.Wi]
}

func (this *Buffer) CopyBytes() []byte {
	ret := make([]byte, this.Available())
	copy(ret, this.Bytes())

	return ret
}

func (this *Buffer) String() string {
	return Bytes2String(this.Buf[this.Ri:this.Wi])
}

func (this *Buffer) Clear() *Buffer {
	this.Ri = 0
	this.Wi = 0
	return this
}

func (this *Buffer) Consume() {
	this.Ri = this.Wi
}

/**
 * @implements io.Writer::Write
 *
 * 将指定 slice 中的数据写入到 Buffer 中。
 * 返回实际写入的字节数。
 */
func (this *Buffer) Write(src []byte) (int, error) {
	this.PutBytes(src)

	return len(src), nil
}

func (this *Buffer) WriteTo(w io.Writer) error {
	return WriteFull(w, this.Bytes())
}

/**
 * @implements io.Reader::Read
 *
 * 从 Buffer 中读取尽可能多数据到 dst 中，直到读完全部数据或者 dst 被填满。
 * 返回实际读出的字节数。
 */
func (this *Buffer) Read(dst []byte) (int, error) {
	var n int
	var err error

	if this.Ri >= this.Wi {
		err = io.EOF
	} else {
		n = copy(dst, this.Buf[this.Ri:this.Wi])
		this.Ri += n
	}

	return n, err
}

func (this *Buffer) ReadFrom(r io.Reader, l int) error {
	this.EnsureWrite(l)

	last := this.Wi + l
	if _, err := io.ReadFull(r, this.Buf[this.Wi:last]); err != nil {
		return err
	}
	this.Wi = last

	return nil
}

func (this *Buffer) PutByte(data byte) {
	if this.AutoGrow {
		this.EnsureWrite(1)
	}
	this.Buf[this.Wi] = data
	this.Wi += 1
}

func (this *Buffer) PutBool(data bool) {
	var b byte = 0
	if data {
		b = 1
	}
	this.PutByte(b)
}

func (this *Buffer) PutUint16(data uint16) {
	if this.AutoGrow {
		this.EnsureWrite(2)
	}
	binary.BigEndian.PutUint16(this.Buf[this.Wi:], data)
	this.Wi += 2
}

func (this *Buffer) PutInt16(data int16) {
	if this.AutoGrow {
		this.EnsureWrite(2)
	}
	binary.BigEndian.PutUint16(this.Buf[this.Wi:], uint16(data))
	this.Wi += 2
}

func (this *Buffer) PutUint32(data uint32) {
	if this.AutoGrow {
		this.EnsureWrite(4)
	}
	binary.BigEndian.PutUint32(this.Buf[this.Wi:], data)
	this.Wi += 4
}

func (this *Buffer) PutInt32(data int32) {
	if this.AutoGrow {
		this.EnsureWrite(4)
	}
	binary.BigEndian.PutUint32(this.Buf[this.Wi:], uint32(data))
	this.Wi += 4
}

func (this *Buffer) PutVUint64(data uint64) {
	if this.AutoGrow {
		this.EnsureWrite(9)
	}
	this.Wi += binary.PutUvarint(this.Buf[this.Wi:], data)
}

func (this *Buffer) PutVInt64(data int64) {
	if this.AutoGrow {
		this.EnsureWrite(9)
	}
	this.Wi += binary.PutVarint(this.Buf[this.Wi:], data)
}

func (this *Buffer) PutVUint(data uint) {
	this.PutVUint64(uint64(data))
}

func (this *Buffer) PutVInt(data int) {
	this.PutVInt64(int64(data))
}

func (this *Buffer) PutBytes(data []byte) {
	if this.AutoGrow {
		this.EnsureWrite(len(data))
	}
	this.Wi += copy(this.Buf[this.Wi:], data)
}

func (this *Buffer) PutVBytes(data []byte) {
	l := len(data)
	this.PutVUint64(uint64(l))
	if this.AutoGrow {
		this.EnsureWrite(l)
	}
	this.Wi += copy(this.Buf[this.Wi:], data)
}

func (this *Buffer) PutStr(data ...string) {
	for _, s := range data {
		if this.AutoGrow {
			this.EnsureWrite(len(data))
		}
		this.Wi += copy(this.Buf[this.Wi:], s)
	}
}

func (this *Buffer) PutVStr(data ...string) {
	for _, s := range data {
		l := len(s)
		this.PutVUint64(uint64(l))
		if this.AutoGrow {
			this.EnsureWrite(l)
		}
		this.Wi += copy(this.Buf[this.Wi:], s)
	}
}

func (this *Buffer) GetByte() (byte, error) {
	if this.Wi-this.Ri < 1 {
		return 0, errNotEnoughData
	}
	result := this.Buf[this.Ri]
	this.Ri += 1

	return result, nil
}

func (this *Buffer) GetBool() (bool, error) {
	b, err := this.GetByte()
	return b == 1, err
}

func (this *Buffer) GetUint16() (uint16, error) {
	if this.Wi-this.Ri < 2 {
		return 0, errNotEnoughData
	}
	result := binary.BigEndian.Uint16(this.Buf[this.Ri:this.Wi])
	this.Ri += 2

	return result, nil
}

func (this *Buffer) GetInt16() (int16, error) {
	if this.Wi-this.Ri < 2 {
		return 0, errNotEnoughData
	}
	result := binary.BigEndian.Uint16(this.Buf[this.Ri:this.Wi])
	this.Ri += 2

	return int16(result), nil
}

func (this *Buffer) GetUint32() (uint32, error) {
	if this.Wi-this.Ri < 4 {
		return 0, errNotEnoughData
	}
	result := binary.BigEndian.Uint32(this.Buf[this.Ri:this.Wi])
	this.Ri += 4

	return result, nil
}

func (this *Buffer) GetInt32() (int32, error) {
	if this.Wi-this.Ri < 4 {
		return 0, errNotEnoughData
	}
	result := binary.BigEndian.Uint32(this.Buf[this.Ri:this.Wi])
	this.Ri += 4

	return int32(result), nil
}

func (this *Buffer) GetVUint64() (uint64, error) {
	result, n := binary.Uvarint(this.Buf[this.Ri:this.Wi])
	if n <= 0 {
		return 0, errNotEnoughData
	}
	this.Ri += n

	return result, nil
}

func (this *Buffer) GetVInt64() (int64, error) {
	result, n := binary.Varint(this.Buf[this.Ri:this.Wi])
	if n <= 0 {
		return 0, errNotEnoughData
	}
	this.Ri += n

	return result, nil
}

func (this *Buffer) GetVUint() (uint, error) {
	u, err := this.GetVUint64()
	return uint(u), err
}
func (this *Buffer) GetVInt() (int, error) {
	i, err := this.GetVInt64()
	return int(i), err
}

func (this *Buffer) GetBytes(n int) ([]byte, error) {
	last := this.Ri + n
	if last > this.Wi {
		return nil, errNotEnoughData
	}
	result := this.Buf[this.Ri:last]
	this.Ri = last

	return result, nil
}

func (this *Buffer) GetVBytes() ([]byte, error) {
	l, err := this.GetVUint64()
	if err != nil {
		return nil, err
	}

	result, err := this.GetBytes(int(l))
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (this *Buffer) GetStr(n int) (string, error) {
	last := this.Ri + n
	if last > this.Wi {
		return "", errNotEnoughData
	}
	result := this.Buf[this.Ri:last]
	this.Ri = last

	return Bytes2String(result), nil
}

func (this *Buffer) GetVStr() (string, error) {
	l, err := this.GetVUint64()
	if err != nil {
		return "", err
	}

	result, err := this.GetBytes(int(l))
	if err != nil {
		return "", err
	}

	return Bytes2String(result), nil
}

func (this *Buffer) PeekStr(n int) (string, error) {
	last := this.Ri + n
	if last > this.Wi {
		return "", errNotEnoughData
	}
	result := this.Buf[this.Ri:last]

	return Bytes2String(result), nil
}

/**
 * 将指定 buf 作为自己的字节缓冲，并将其视为已填满内容，做好从头开始读取的准备。
 *
 * 典型用法：
 *		var xb xbuf.Buffer
 *      xb.WrapForRead(buf)  // 避免了对 Buffer 的动态内存分配
 */
func (this *Buffer) WrapForRead(buf []byte) {
	this.Buf = buf
	this.Ri = 0
	this.Wi = len(buf)
	this.AutoGrow = false
}

/**
 * 将指定 buf 作为自己的字节缓冲，并将其视为尚未有内容，做好从头开始写入的准备。
 */
func (this *Buffer) WrapForWrite(buf []byte, autoGrow bool) {
	this.Buf = buf
	this.Ri = 0
	this.Wi = 0
	this.AutoGrow = autoGrow
}

//=============================================================================

func New(size int) *Buffer {
	return &Buffer{make([]byte, size), 0, 0, true}
}

func WrapForRead(buf []byte) *Buffer {
	return &Buffer{buf, 0, len(buf), false}
}

func WrapForWrite(buf []byte, autoGrow bool) *Buffer {
	return &Buffer{buf, 0, 0, autoGrow}
}
